home *** CD-ROM | disk | FTP | other *** search
/ Mac Mania 2 / MacMania 2.toast / Demo's / Tools&Utilities / Programming / MacStarter Pascal 1.0 / xWindows definition files / xScaledGraphicsDecoration.p < prev    next >
Encoding:
Text File  |  1993-12-11  |  23.2 KB  |  756 lines  |  [TEXT/PJMM]

  1. unit xScaledGraphicsDecoration;
  2.  
  3. { This unit defines a sub-class of xWindowDecoration.  A decoration in this subclass provides }
  4. { methods for doing some basic drawing operations using real coordinates instead of the }
  5. { usual QuickDraw integer coordinates.   Coordinates in these drawing commands are }
  6. { automatically scaled to the current decoration size.   Furthermore, when you use these }
  7. { commands, graphics output is automatically "clipped" so that nothing is drawn outside }
  8. { the decoration itself.  (The cost of all this convenience will  be some decrease in speed }
  9. { of drawing operations. ) }
  10. {     A further subclass is defined which includes automatic display of a set of }
  11. { coordinate axes in the decoration. }
  12.  
  13. { NOTE: This UNIT is here for use in the sample project "SimpleGraph".  It works OK, but }
  14. {            really doesn't do the right thing about clipping; that is, it always sets the clip rect}
  15. {            everytime something is drawn into a ScaledGraphicsDecoration, then resets it to }
  16. {            the ENTIRE plane.  This should probably be modified so that it will only do this if }
  17. {            some "autoclip" feature is turned on. }
  18.  
  19. interface
  20.  
  21. uses
  22.     xWindow;
  23.  
  24. type
  25.     xScaledGraphics = object(xWindowDecoration)
  26.  
  27.             xScale, yScale: extended;   { factors used in scaling calculations }
  28.             xmin, xmax, ymin, ymax: extended;  { range of real-number coordinates for decoration; }
  29.               { Set by default when the decoration is created to 0,1,0,1. }
  30.             useNormalWindow: boolean;  { set to true if user sets coordinates using }
  31.               { SetNormalCoordinates instead of SetCoordinates; false by default }
  32.             xmin_normal, xmax_normal, ymin_normal, ymax_normal: extended;
  33.               { save the parameters used in SetNormalCoordinates }
  34.             fillPattern: Pattern;  { pattern to be used in filling objects; solid black by default }
  35.  
  36.             procedure Setup (win: xWindow;
  37.                                         theLeft, theTop, theWidth, theHeight: integer);
  38.              { Installs the decoration in the specified xWindow.  The remaining parameters }
  39.              { specify the location and size of the decoration, as described in the comment }
  40.              { for procedure xWindowDecoration.Install in the file xWindow.p }
  41.             procedure SetCoordinates (newXMin, newXMax, newYMin, newYMax: extended);
  42.              { Establishes that (newXMin, newYMin) will be the coordinates of the lower }
  43.              { left corner of the decoration, and (newXMax,NewYMax) the coordinates of the }
  44.              { upper right.  All coordinates used in other procedures will be scaled to this }
  45.              { range.  This procedure will generate an update event to see that the contents }
  46.              { of the decoration are re-drawn in the new coordinate system. }
  47.              {    NOTE:  The default coordinates are set up with SetCoordinates(0,1,0,1) }
  48.              { when the decoration is SetUp. }
  49.             procedure SetNormalCoordinates (newXMin, newXMax, newYMin, newYMax: extended);
  50.              { Like SetCoordinates, this establishes the scaling to be used.  In this case, however, }
  51.              { the scaling will refer to the LARGEST SQUARE that will fit in the decoration. }
  52.              { The square will be centered horizontally or vertically, as appropriate.  Points }
  53.              { in the window outside this square will have coordinates outside the range }
  54.              { newXMin to newXMax or outside newYMin to newYMax. }
  55.              {     This procedure will generate an update event to see that the contents }
  56.              { of the decoration are re-drawn in the new coordinate system. }
  57.             procedure drawLine (x1, y1, x2, y2: extended);
  58.              { Draw a line from (x1,y1) to (x2,y2), IN THE REAL COORDINATE SYSTEM }
  59.              { ESTABLISHED FOR THE DECORATION. }
  60.             procedure drawRectangle (x1, y1, x2, y2: extended);
  61.              { Draw the retangle (sides only) with corners at (x1,y1) and (x2,y2). }
  62.             procedure drawRoundRect (x1, y1, x2, y2: extended);
  63.              { Draw a retangle  with rounded corners at (x1,y1) and (x2,y2). }
  64.             procedure drawOval (x1, y1, x2, y2: extended);
  65.              { Draw the oval that just fits in the rectangle with corners at (x1,y1) and (x2,y2). }
  66.             procedure drawCircle (xCenter, yCenter, radius: extended);
  67.              { Draw a circle with specifed center and radius; if the horizontal scale is different }
  68.              { from the vertical, then the horizontal scale is used to determine the radius drawn }
  69.             procedure drawFilledRectangle (x1, y1, x2, y2: extended);
  70.             procedure drawFilledRoundRect (x1, y1, x2, y2: extended);
  71.             procedure drawFilledOval (x1, y1, x2, y2: extended);
  72.             procedure drawFilledCircle (xCenter, yCenter, radius: extended);
  73.              { These four procedures draw the same figures as their unfilled counterparts, }
  74.              { then fill them with the current fill pattern (solid black by default) }
  75.             procedure clearRectangle (x1, y1, x2, y2: extended);
  76.             procedure clearRoundRect (x1, y1, x2, y2: extended);
  77.             procedure clearOval (x1, y1, x2, y2: extended);
  78.             procedure clearCircle (xCenter, yCenter, radius: extended);
  79.              { These four procedures erase the figures by painting the area occupied by }
  80.              { the figure white; this is different from drawing a filled figure with a white }
  81.              { pattern, since in that case the outline of the figure will still be drawn. }
  82.             procedure SetFillPattern (pat: pattern);
  83.              { Set the current fill pattern to the specified pattern; You can use one of the }
  84.              { Standard patterns: black, gray, ltGray, dkGray, white.  You could also get the }
  85.              { pattern from a pattern resource. }
  86.             procedure SetFillPatternNumber (patternIndex: integer);
  87.              { Set the current fill pattern to one of the 38 standard patterns; the parameter }
  88.              { patternIndex is a number between 1 and 38 specifying the pattern. }
  89.             procedure XY2HV (x, y: extended;
  90.                                         var h, v: integer);
  91.              { Does the scaling of real numbers to standard integer coordinates }
  92.             procedure HV2XY (h, v: integer;
  93.                                         var x, y: extended);
  94.              { Does the reverse scaling of standard window coordinates to real numbers. }
  95.              { This might be useful if you decide to override the doContentClick Procedure.  You  }
  96.              { can use it to convert the localPt parameter of that procedure to the real }
  97.              { number coordinates of the decoration. }
  98.             procedure XYRect2HVRect (x1, y1, x2, y2: extended;
  99.                                         var R: Rect);
  100.             { Converts a rectangle specification in real number coordinates to a standard }
  101.             { integer-coordinate Rect. }
  102.             procedure adjustSize;
  103.             override;
  104.             { called when the size of the window changes; updates instance variables }
  105.             { appropriately  }
  106.         end;
  107.  
  108.     xScaledGraphicsWithAxes = object(xScaledGraphics)
  109.             { This subclass implements a version of xScaledGraphics that includes an }
  110.             { automatically displayed set of coordinate axes.   The axes are labeled a }
  111.             { according to the range of values currently set by SetCoordinates or }
  112.             { SetNormalCoordinates.  All of the methods for this class have the same }
  113.             { description as those in the parent class, except that they are extended to }
  114.             { compute and/or draw the axes, as appropriate. }
  115.             theAxes: PicHandle;  { the axes }
  116.             procedure Setup (win: xWindow;
  117.                                         theLeft, theTop, theWidth, theHeight: integer);
  118.             override;
  119.             procedure SetCoordinates (newXMin, newXMax, newYMin, newYMax: extended);
  120.             override;
  121.             procedure SetNormalCoordinates (newXMin, newXMax, newYMin, newYMax: extended);
  122.             override;
  123.             procedure doDraw;
  124.             override;
  125.             procedure MakeAxes;
  126.         end;
  127.  
  128. implementation
  129.  
  130. procedure xScaledGraphics.Setup (win: xWindow;
  131.                                 theLeft, theTop, theWidth, theHeight: integer);
  132.     begin
  133.         init;
  134.         xmin := 0;
  135.         xmax := 1;
  136.         ymin := 0;
  137.         ymax := 1;
  138.         fillPattern := black;
  139.         useNormalWindow := false;
  140.         Install(win, theLeft, theTop, theWidth, theHeight);
  141.     end;
  142.  
  143. procedure xScaledGraphics.SetCoordinates (newXMin, newXMax, newYMin, newYMax: extended);
  144.     begin
  145.         if (newXMin = newXMax) | (newYmin = newYMax) then
  146.             EXIT(SetCoordinates);
  147.         forceRedraw;
  148.         xmin := newXMin;
  149.         ymin := newYMin;
  150.         xmax := newXMax;
  151.         ymax := newYMax;
  152.         xScale := (clickRect.right - clickRect.left) / (newXMax - newXMin);
  153.         yScale := (clickRect.bottom - clickRect.top) / (newYMax - newYMin);
  154.         useNormalWindow := false;
  155.     end;
  156.  
  157. procedure xScaledGraphics.SetNormalCoordinates (newXMin, newXMax, newYMin, newYMax: extended);
  158.     var
  159.         h, w: integer;
  160.         excess: extended;
  161.     begin
  162.         if (newXMin = newXMax) | (newYMIN = newYMax) then
  163.             EXIT(SetNormalCoordinates);
  164.         forceRedraw;
  165.         w := clickRect.right - clickRect.left;
  166.         h := clickRect.bottom - clickRect.top;
  167.         xmin_normal := newXMin;
  168.         ymin_normal := newYMin;
  169.         xmax_normal := newXMax;
  170.         ymax_normal := newYMax;
  171.         if w > h then begin
  172.                 excess := (w - h) / h * (newXMax - newXMin) / 2;
  173.                 newXMin := newXMin - excess;
  174.                 newXMax := newXMax + excess;
  175.             end
  176.         else begin
  177.                 excess := (h - w) / w * (newYMax - newYMin) / 2;
  178.                 newYMax := newYMax + excess;
  179.                 newYMin := newYMin - excess;
  180.             end;
  181.         xmin := newXMin;
  182.         ymin := newYMin;
  183.         xmax := newXMax;
  184.         ymax := newYMax;
  185.         xScale := (clickRect.right - clickRect.left) / (newXMax - newXMin);
  186.         yScale := (clickRect.bottom - clickRect.top) / (newYMax - newYMin);
  187.         useNormalWindow := true;
  188.     end;
  189.  
  190.  
  191. procedure xScaledGraphics.drawLine (x1, y1, x2, y2: extended);
  192.     var
  193.         savePort: GrafPtr;
  194.         h1, h2, v1, v2: integer;
  195.     begin
  196.         GetPort(savePort);
  197.         SetPort(itsWindow.theWindow);
  198.         ClipRect(clickRect);
  199.         XY2HV(x1, y1, h1, v1);
  200.         XY2HV(x2, y2, h2, v2);
  201.         MoveTo(h1, v1);
  202.         LineTo(h2, v2);
  203.         ClipRect(itsWindow.theWindow^.portRect);
  204.         SetPort(savePort);
  205.     end;
  206.  
  207. procedure xScaledGraphics.drawRectangle (x1, y1, x2, y2: extended);
  208.     var
  209.         R: Rect;
  210.         savePort: GrafPtr;
  211.     begin
  212.         GetPort(savePort);
  213.         SetPort(itsWindow.theWindow);
  214.         ClipRect(clickRect);
  215.         XYRect2HVRect(x1, y1, x2, y2, R);
  216.         FrameRect(R);
  217.         ClipRect(itsWindow.theWindow^.portRect);
  218.         SetPort(savePort);
  219.     end;
  220.  
  221. procedure xScaledGraphics.drawRoundRect (x1, y1, x2, y2: extended);
  222.     var
  223.         R: Rect;
  224.         savePort: GrafPtr;
  225.     begin
  226.         GetPort(savePort);
  227.         SetPort(itsWindow.theWindow);
  228.         ClipRect(clickRect);
  229.         XYRect2HVRect(x1, y1, x2, y2, R);
  230.         FrameRoundRect(R, 16, 16);
  231.         ClipRect(itsWindow.theWindow^.portRect);
  232.         SetPort(savePort);
  233.     end;
  234.  
  235. procedure xScaledGraphics.drawOval (x1, y1, x2, y2: extended);
  236.     var
  237.         R: Rect;
  238.         savePort: GrafPtr;
  239.     begin
  240.         GetPort(savePort);
  241.         SetPort(itsWindow.theWindow);
  242.         ClipRect(clickRect);
  243.         XYRect2HVRect(x1, y1, x2, y2, R);
  244.         FrameOval(R);
  245.         ClipRect(itsWindow.theWindow^.portRect);
  246.         SetPort(savePort);
  247.     end;
  248.  
  249. procedure xScaledGraphics.drawCircle (xCenter, yCenter, radius: extended);
  250.     var
  251.         R: Rect;
  252.         h, v: integer;
  253.         size: extended;
  254.         savePort: GrafPtr;
  255.     begin
  256.         GetPort(savePort);
  257.         SetPort(itsWindow.theWindow);
  258.         ClipRect(clickRect);
  259.         XY2HV(xCenter, yCenter, h, v);
  260.         size := abs(radius) * xScale;
  261.         if (size < maxint) & (abs(h - size) < maxint) & (abs(h + size) < maxint) & (abs(v - size) < maxint) & (abs(v + size) < maxint) then begin
  262.                 SetRect(R, h - round(size), v - round(size), h + round(size), v + round(size));
  263.                 FrameOval(R);
  264.             end;
  265.         ClipRect(itsWindow.theWindow^.portRect);
  266.         SetPort(savePort);
  267.     end;
  268.  
  269. procedure xScaledGraphics.drawFilledRectangle (x1, y1, x2, y2: extended);
  270.     var
  271.         R: Rect;
  272.         savePort: GrafPtr;
  273.     begin
  274.         GetPort(savePort);
  275.         SetPort(itsWindow.theWindow);
  276.         ClipRect(clickRect);
  277.         XYRect2HVRect(x1, y1, x2, y2, R);
  278.         FillRect(R, fillPattern);
  279.         FrameRect(R);
  280.         ClipRect(itsWindow.theWindow^.portRect);
  281.         SetPort(savePort);
  282.     end;
  283.  
  284. procedure xScaledGraphics.drawFilledRoundRect (x1, y1, x2, y2: extended);
  285.     var
  286.         R: Rect;
  287.         savePort: GrafPtr;
  288.     begin
  289.         GetPort(savePort);
  290.         SetPort(itsWindow.theWindow);
  291.         ClipRect(clickRect);
  292.         XYRect2HVRect(x1, y1, x2, y2, R);
  293.         FillRoundRect(R, 16, 16, fillPattern);
  294.         FrameRoundRect(R, 16, 16);
  295.         ClipRect(itsWindow.theWindow^.portRect);
  296.         SetPort(savePort);
  297.     end;
  298.  
  299. procedure xScaledGraphics.drawFilledOval (x1, y1, x2, y2: extended);
  300.     var
  301.         R: Rect;
  302.         savePort: GrafPtr;
  303.     begin
  304.         GetPort(savePort);
  305.         SetPort(itsWindow.theWindow);
  306.         ClipRect(clickRect);
  307.         XYRect2HVRect(x1, y1, x2, y2, R);
  308.         FillOval(R, fillPattern);
  309.         FrameOval(R);
  310.         ClipRect(itsWindow.theWindow^.portRect);
  311.         SetPort(savePort);
  312.     end;
  313.  
  314. procedure xScaledGraphics.drawFilledCircle (xCenter, yCenter, radius: extended);
  315.     var
  316.         R: Rect;
  317.         h, v: integer;
  318.         size: extended;
  319.         savePort: GrafPtr;
  320.     begin
  321.         GetPort(savePort);
  322.         SetPort(itsWindow.theWindow);
  323.         ClipRect(clickRect);
  324.         XY2HV(xCenter, yCenter, h, v);
  325.         size := abs(radius) * xScale;
  326.         if (size < maxint) & (abs(h - size) < maxint) & (abs(h + size) < maxint) & (abs(v - size) < maxint) & (abs(v + size) < maxint) then begin
  327.                 SetRect(R, h - round(size), v - round(size), h + round(size), v + round(size));
  328.                 FillOval(R, fillPattern);
  329.                 FrameOval(R);
  330.             end;
  331.         ClipRect(itsWindow.theWindow^.portRect);
  332.         SetPort(savePort);
  333.     end;
  334.  
  335. procedure xScaledGraphics.clearRectangle (x1, y1, x2, y2: extended);
  336.     var
  337.         R: Rect;
  338.         savePort: GrafPtr;
  339.     begin
  340.         GetPort(savePort);
  341.         SetPort(itsWindow.theWindow);
  342.         ClipRect(clickRect);
  343.         XYRect2HVRect(x1, y1, x2, y2, R);
  344.         EraseRect(R);
  345.         ClipRect(itsWindow.theWindow^.portRect);
  346.         SetPort(savePort);
  347.     end;
  348.  
  349. procedure xScaledGraphics.clearRoundRect (x1, y1, x2, y2: extended);
  350.     var
  351.         R: Rect;
  352.         savePort: GrafPtr;
  353.     begin
  354.         GetPort(savePort);
  355.         SetPort(itsWindow.theWindow);
  356.         ClipRect(clickRect);
  357.         XYRect2HVRect(x1, y1, x2, y2, R);
  358.         EraseRoundRect(R, 16, 16);
  359.         ClipRect(itsWindow.theWindow^.portRect);
  360.         SetPort(savePort);
  361.     end;
  362.  
  363. procedure xScaledGraphics.clearOval (x1, y1, x2, y2: extended);
  364.     var
  365.         R: Rect;
  366.         savePort: GrafPtr;
  367.     begin
  368.         GetPort(savePort);
  369.         SetPort(itsWindow.theWindow);
  370.         ClipRect(clickRect);
  371.         XYRect2HVRect(x1, y1, x2, y2, R);
  372.         EraseOval(R);
  373.         ClipRect(itsWindow.theWindow^.portRect);
  374.         SetPort(savePort);
  375.     end;
  376.  
  377. procedure xScaledGraphics.clearCircle (xCenter, yCenter, radius: extended);
  378.     var
  379.         R: Rect;
  380.         h, v: integer;
  381.         size: extended;
  382.         savePort: GrafPtr;
  383.     begin
  384.         GetPort(savePort);
  385.         SetPort(itsWindow.theWindow);
  386.         ClipRect(clickRect);
  387.         XY2HV(xCenter, yCenter, h, v);
  388.         size := abs(radius) * xScale;
  389.         if (size < maxint) & (abs(h - size) < maxint) & (abs(h + size) < maxint) & (abs(v - size) < maxint) & (abs(v + size) < maxint) then begin
  390.                 SetRect(R, h - round(size), v - round(size), h + round(size), v + round(size));
  391.                 EraseOval(R);
  392.             end;
  393.         ClipRect(itsWindow.theWindow^.portRect);
  394.         SetPort(savePort);
  395.     end;
  396.  
  397. procedure xScaledGraphics.SetFillPatternNumber (patternIndex: integer);
  398.     begin
  399.         if (patternIndex > 0) & (patternIndex <= 38) then
  400.             GetIndPattern(fillPattern, sysPatListID, patternIndex);
  401.     end;
  402.  
  403. procedure xScaledGraphics.SetFillPattern (pat: pattern);
  404.     begin
  405.         fillPattern := pat;
  406.     end;
  407.  
  408. procedure xScaledGraphics.XY2HV (x, y: extended;
  409.                                 var h, v: integer);
  410.     var
  411.         xInt, yInt: extended;
  412.         size: extended;
  413.     begin
  414.         xInt := clickRect.left + (x - xMin) * xScale;
  415.         yInt := clickRect.bottom - (y - yMin) * yScale;
  416.         if (abs(yInt) < 20000) & (abs(xInt) < 20000) then begin
  417.                 h := round(xInt);
  418.                 v := round(yInt);
  419.             end
  420.         else begin
  421.                 size := 20000 / sqrt(sqr(xInt) + sqr(yInt));
  422.                 h := round(xInt * size);
  423.                 v := round(yInt * size);
  424.             end;
  425.     end;
  426.  
  427. procedure xScaledGraphics.HV2XY (h, v: integer;
  428.                                 var x, y: extended);
  429.     begin
  430.         x := xmin + (h - clickRect.left) / xScale;
  431.         y := ymin - (v - clickRect.bottom) / yScale;
  432.     end;
  433.  
  434. procedure xScaledGraphics.XYRect2HVRect (x1, y1, x2, y2: extended;
  435.                                 var R: Rect);
  436.     var
  437.         pt1, pt2: point;
  438.     begin
  439.         XY2HV(x1, y1, pt1.h, pt1.v);
  440.         XY2HV(x2, y2, pt2.h, pt2.v);
  441.         Pt2Rect(pt1, pt2, R);
  442.     end;
  443.  
  444. procedure xScaledGraphics.adjustSize;
  445.     begin
  446.         inherited AdjustSize;
  447.         if useNormalWindow then
  448.             SetNormalCoordinates(xmin_normal, xmax_normal, ymin_normal, ymax_normal)
  449.         else
  450.             SetCoordinates(xmin, xmax, ymin, ymax);
  451.     end;
  452.  
  453.  
  454. function FudgeStart (a, b: extended): extended;
  455. { tries to find a "rounded value" close to a, close to within 5% of b-a }
  456.     var
  457.         diff: extended;
  458.         ans: extended;
  459.         len: integer;
  460.         str: string;
  461.     begin
  462.         diff := abs(0.05 * (b - a));
  463.         if abs(round(a) - a) < diff then
  464.             FudgeStart := round(a)
  465.         else if abs(round(a * 10) / 10 - a) < diff then
  466.             FudgeStart := round(a * 10) / 10
  467.         else begin
  468.                 len := 8;
  469.                 repeat
  470.                     str := StringOf(a : len);
  471.                     ReadString(str, ans);
  472.                     len := len + 1;
  473.                 until (abs(a - ans) < diff) | (len = 30);
  474.                 FudgeStart := ans;
  475.             end;
  476.     end;
  477.  
  478.  
  479. function Fudge (x: extended): extended;
  480. { move x to a more "rounded" value; used for labeling axes }
  481.     var
  482.         i, digits: integer;
  483.         y: extended;
  484.     begin
  485.         if (ABS(x) < 0.0005) or (ABS(x) > 500000) then
  486.             fudge := x
  487.         else if (abs(x) < 0.1) | (abs(x) > 5000) then begin
  488.                 y := x;
  489.                 digits := 0;
  490.                 if abs(y) >= 8.875 then
  491.                     while abs(y) >= 8.75 do begin
  492.                             y := y / 10;
  493.                             digits := digits + 1
  494.                         end
  495.                 else if abs(y) < 0.875 then
  496.                     while abs(y) < 1 do begin
  497.                             y := y * 10;
  498.                             digits := digits - 1
  499.                         end;
  500.                 y := round(y * 4) / 4;
  501.                 if digits > 0 then
  502.                     for i := 1 to digits do
  503.                         y := y * 10
  504.                 else if digits < 0 then
  505.                     for i := 1 to -digits do
  506.                         y := y / 10;
  507.                 fudge := y
  508.             end
  509.         else if abs(x) < 0.5 then
  510.             fudge := round(10 * x) / 10
  511.         else if abs(x) < 2.5 then
  512.             fudge := round(2 * x) / 2
  513.         else if abs(x) < 12 then
  514.             fudge := round(x)
  515.         else if abs(x) < 120 then
  516.             fudge := round(x / 10) * 10
  517.         else if abs(x) < 1200 then
  518.             fudge := round(x / 100) * 100
  519.         else
  520.             fudge := round(x / 1000) * 1000
  521.     end;
  522.  
  523.  
  524. {$PUSH}
  525. {$R-}
  526.  
  527. procedure RealToString (x: extended;   {exported; described above}
  528.                                 var s: string);
  529.     var
  530.         n, i: integer;
  531.     begin
  532.         if (abs(x) >= 5e8) or (abs(x) < 5e-8) then begin  { exponential form }
  533.                 n := 15;
  534.                 repeat  { this is needed since the stupid computer alllows 4 spaces for the exponent even if it is one two or three digits }
  535.                     s := StringOf(x : n);
  536.                     n := n - 1;
  537.                     i := length(s);
  538.                     while (i > 0) & (s[i] = ' ') do
  539.                         i := i - 1;
  540.                     s[0] := chr(i);
  541.                 until (length(s) <= 12) | (n = 11)
  542.             end
  543.         else begin
  544.                 s := StringOf(x : 1 : 10);
  545.                 i := length(s);
  546.                 while (i > 0) & (s[i] = '0') do   { strip off trailing zeros }
  547.                     i := i - 1;
  548.                 if (i > 0) & (s[i] = '.') then  { strip off terminating decimal point }
  549.                     i := i - 1;
  550.                 if i > 12 then  { maximum length allowed for output is 12}
  551.                     s[0] := chr(12)
  552.                 else
  553.                     s[0] := chr(i);
  554.             end
  555.     end;
  556.  
  557. {$POP}
  558.  
  559. procedure DrawStandardAxes (xmin, xmax, ymin, ymax: extended;
  560.                                 left, top: integer;
  561.                                 width, height: integer);
  562. { draw axes with labeled tic marks }
  563.     var
  564.         Labels: array[1..20] of string;
  565.         LabelLocs: array[1..20] of integer;
  566.         LabelRef: integer;
  567.         maxsize: integer;
  568.         labelct, i, w: integer;
  569.         x, y: extended;
  570.         interval: extended;
  571.         LabelsOnLeft: boolean;
  572.         xStart, yStart: extended;
  573.         xAxisLoc, yAxisLoc: integer;
  574.     begin
  575.         if (xmin >= 0) | (xmax < 0) then begin
  576.                 yAxisLoc := 0;
  577.                 xStart := FudgeStart(xmin, xmax);
  578.             end
  579.         else begin
  580.                 yaxisLoc := trunc(-xmin / (xmax - xmin) * width);
  581.                 xStart := 0;
  582.             end;
  583.         if (ymin >= 0) | (ymax < 0) then begin
  584.                 xAxisLoc := height;
  585.                 yStart := FudgeStart(ymin, ymax);
  586.             end
  587.         else begin
  588.                 xaxisLoc := height - trunc(-ymin / (ymax - ymin) * height);
  589.                 yStart := 0;
  590.             end;
  591.         MoveTo(left, top + xAxisLoc - 1);
  592.         if (xmin > 0) | (xmax <= 0) then begin
  593.                 if xmin > 0 then
  594.                     Line(5, 0)
  595.                 else
  596.                     Move((5), 0);
  597.                 Line((2), 0);
  598.                 Move((3), 0);
  599.                 Line((2), 0);
  600.                 Move((3), 0);
  601.                 Line((2), 0);
  602.                 Move((3), 0);
  603.             end;
  604.         LineTo(left + width, top + xAxisLoc - 1);
  605.         MoveTo(left + yAxisLoc, top + height);
  606.         if (ymin > 0) | (ymax <= 0) then begin
  607.                 if ymin > 0 then
  608.                     Line(0, -(5))
  609.                 else
  610.                     Move(0, -(5));
  611.                 Line(0, -(2));
  612.                 Move(0, -(3));
  613.                 Line(0, -(2));
  614.                 Move(0, -(3));
  615.                 Line(0, -(2));
  616.                 Move(0, -(3));
  617.             end;
  618.         LineTo(left + yAxisLoc, top);
  619.         labelct := height div (40);
  620.         if labelct <= 2 then
  621.             labelct := 3;
  622.         interval := fudge((ymax - ymin) / labelct);
  623.         y := yStart + interval;
  624.         labelct := 0;
  625.         maxsize := 0;
  626.         while y < ymax do begin
  627.                 labelct := labelct + 1;
  628.                 RealToString(y, Labels[labelct]);
  629.                 w := StringWidth(Labels[labelct]);
  630.                 if w > maxsize then
  631.                     maxsize := w;
  632.                 LabelLocs[labelct] := round(height - (y - ymin) / (ymax - ymin) * height);
  633.                 y := y + interval
  634.             end;
  635.         y := yStart - interval;
  636.         while y > ymin do begin
  637.                 labelct := labelct + 1;
  638.                 RealToString(y, Labels[labelct]);
  639.                 w := StringWidth(Labels[labelct]);
  640.                 if w > maxsize then
  641.                     maxsize := w;
  642.                 LabelLocs[labelct] := round(height - (y - ymin) / (ymax - ymin) * height);
  643.                 y := y - interval
  644.             end;
  645.         if yAxisLoc > maxsize + (8) then begin
  646.                 LabelRef := yAxisLoc - (8);
  647.                 LabelsOnLeft := true
  648.             end
  649.         else begin
  650.                 LabelRef := yAxisLoc + (8);
  651.                 LabelsOnLeft := false
  652.             end;
  653.         for i := 1 to labelct do begin
  654.                 MoveTo(left + yaxisloc, top + LabelLocs[i]);
  655.                 Line((3), 0);
  656.                 Line(-(7), 0);
  657.                 MoveTo(left + LabelRef, thePort^.pnLoc.v + (4));
  658.                 if LabelsOnLeft then
  659.                     Move(-StringWidth(Labels[i]), 0);
  660.                 DrawString(Labels[i])
  661.             end;
  662.         labelct := width div (75);
  663.         if labelct <= 2 then
  664.             labelct := 3;
  665.         interval := fudge((xmax - xmin) / labelct);
  666.         x := xStart + interval;
  667.         labelct := 0;
  668.         while x < xmax do begin
  669.                 labelct := labelct + 1;
  670.                 RealToString(x, Labels[labelct]);
  671.                 LabelLocs[labelct] := round((x - xmin) / (xmax - xmin) * width);
  672.                 x := x + interval
  673.             end;
  674.         x := xStart - interval;
  675.         while x > xmin do begin
  676.                 labelct := labelct + 1;
  677.                 RealToString(x, Labels[labelct]);
  678.                 LabelLocs[labelct] := round((x - xmin) / (xmax - xmin) * width);
  679.                 x := x - interval
  680.             end;
  681.         if xaxisloc <= (height - (12)) then begin
  682.                 LabelRef := xaxisloc + (17);
  683.             end
  684.         else begin
  685.                 LabelRef := xaxisloc - (8);
  686.             end;
  687.         for i := 1 to labelct do begin
  688.                 MoveTo(left + LabelLocs[i], top + xaxisloc);
  689.                 Line(0, (3));
  690.                 Line(0, -(7));
  691.                 MoveTo(thePort^.pnLoc.h, top + LabelRef);
  692.                 Move(-(StringWidth(Labels[i]) div 2), 0);
  693.                 DrawString(Labels[i])
  694.             end;
  695.     end;
  696.  
  697. procedure xScaledGraphicsWithAxes.Setup (win: xWindow;
  698.                                 theLeft, theTop, theWidth, theHeight: integer);
  699.     begin
  700.         theAxes := nil;
  701.         inherited SetUp(win, theLeft, theTop, theWidth, theHeight);
  702.     end;
  703.  
  704. procedure xScaledGraphicsWithAxes.SetCoordinates (newXMin, newXMax, newYMin, newYMax: extended);
  705.     begin
  706.         inherited SetCoordinates(newXMin, newXMax, newYmin, newYmax);
  707.         MakeAxes;
  708.     end;
  709.  
  710. procedure xScaledGraphicsWithAxes.SetNormalCoordinates (newXMin, newXMax, newYMin, newYMax: extended);
  711.     begin
  712.         inherited SetNormalCoordinates(newXMin, newXMax, newYmin, newYmax);
  713.         MakeAxes;
  714.     end;
  715.  
  716. procedure xScaledGraphicsWithAxes.MakeAxes;
  717.     var
  718.         savePort: GrafPtr;
  719.         lft, tp, wdth, hght: integer;
  720.         txSize: integer;
  721.     begin
  722.         GetPort(savePort);
  723.         SetPort(itsWindow.theWindow);
  724.         ClipRect(itsWindow.theWindow^.portRect);
  725.         if theAxes <> nil then
  726.             KillPicture(theAxes);
  727.         theAxes := OpenPicture(clickRect);
  728.         lft := clickRect.left;
  729.         tp := clickRect.top;
  730.         wdth := clickRect.right - lft;
  731.         hght := clickRect.bottom - tp;
  732.         txSize := itsWindow.theWindow^.txSize;
  733.         TextSize(10);
  734.         ForeColor(magentaColor);
  735.         DrawStandardAxes(xmin, xmax, ymin, ymax, lft, tp, wdth, hght);
  736.         ForeColor(blackColor);
  737.         TextSize(txSize);
  738.         ClosePicture;
  739.         SetPort(savePort);
  740.     end;
  741.  
  742. procedure xScaledGraphicsWithAxes.doDraw;
  743.     var
  744.         R: Rect;
  745.         pic: PicHandle;
  746.     begin
  747.         if theAxes <> nil then begin
  748.                 R := drawRect;
  749.                 pic := theAxes;
  750.                 ClipRect(clickRect);
  751.                 DrawPicture(pic, R);
  752.                 ClipRect(itsWindow.theWindow^.portRect);
  753.             end;
  754.     end;
  755.  
  756. end.